﻿using System;
using System.IO;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Communication.Net.Base;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.Communication.Net.Web
{
    /// <summary>
    /// http连接
    /// </summary>
    public class HttpConnection : IDisposable
    {
        private readonly HttpServer _ownerHttpServer;
        private bool _keepAlive = false;
        private long _lastRequestTimestamp = 0;
        private readonly IHttpRequestHandler _requestHandler;
        private readonly NetLinkInfo _linkInfo;
        private readonly AcceptSocketArgs _args;
        private readonly Socket _client;
        private readonly Stream _stream;
        /// <summary>
        /// 收发数据流
        /// </summary>
        public Stream Stream
        {
            get { return _stream; }
        }


        /// <summary>
        /// 获取指示当前客户端与服务端口的连接状态
        /// </summary>
        public bool Connected
        {
            get
            {
                var client = this._client;
                if (client == null)
                {
                    return false;
                }
                else
                {
                    return client.Connected;
                }
            }
        }

        /// <summary>
        /// 获取当前http请示的连接信息
        /// </summary>
        public NetLinkInfo LinkInfo
        {
            get { return _linkInfo; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="ownerHttpServer">http服务器</param>
        /// <param name="args">http客户端连接参数</param>
        /// <param name="requestHandler">请求处理对象</param>
        public HttpConnection(HttpServer ownerHttpServer, AcceptSocketArgs args, IHttpRequestHandler requestHandler)
        {
            this._ownerHttpServer = ownerHttpServer;
            this._requestHandler = requestHandler;

            this._args = args;
            this._client = args.Client;
            this._linkInfo = new NetLinkInfo(this._client.RemoteEndPoint, this._client.LocalEndPoint);
            this._stream = this.CreateStream(this._client, ownerHttpServer.ServerConfig.Certificate);
            this.UpdateLastRequestTime();

            //连接请求处理
            Task.Factory.StartNew(this.ReceiveHttpRequest);
        }


        /// <summary>
        /// 处理ssl加密请求
        /// </summary>
        /// <param name="client"></param>
        /// <param name="serverCertificate"></param>
        /// <returns></returns>
        private Stream CreateStream(Socket client, X509Certificate serverCertificate)
        {
            //获取流
            var clientStream = new NetworkStream(client);

            //处理SSL
            if (serverCertificate != null)
            {
                var sslStream = new SslStream(clientStream);
                sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.None, true);
                sslStream.ReadTimeout = 10000;
                sslStream.WriteTimeout = 10000;
                return sslStream;
            }
            else
            {
                return clientStream;
            }
        }





        /// <summary>
        /// 接收并处理请求
        /// </summary>
        private void ReceiveHttpRequest()
        {
            bool upgradeWebSocket = false;
            try
            {
                //构造HTTP响应
                IHttpResponse response = null;
                string tempFileDirectory = this._ownerHttpServer.ServerConfig.GetTempFileDirectory();
                byte[] buffer = new byte[NetHelper.TCP_PACKAGE];
                int durationZeroCount = 0;

                while (this._stream.CanRead)
                {
                    int readLength = this._stream.Read(buffer, 0, buffer.Length);
                    if (readLength <= 0)
                    {
                        durationZeroCount++;
                        if (durationZeroCount >= WebConstnats.CONTINUITY_READ_DATA_LEN_ZERO_COUNT)
                        {
                            //当连续收到5次长为0 的数据,则断开连接.通常情况下是客户端断开了连接,但是服务端并不知道
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }
                    durationZeroCount = 0;
                    this.UpdateLastRequestTime();

                    try
                    {
                        //构造HTTP请求
                        var request = new HttpRequest(this, tempFileDirectory, buffer, readLength);

                        //判断是否需要保持连接
                        if (request.Headers.TryGetValue(HttpRequestHeaders.Connection, out string con))
                        {
                            if (string.Equals(con, HttpRequestHeaders.KeepAlive, StringComparison.OrdinalIgnoreCase))
                            {
                                this._keepAlive = true;
                            }
                        }

                        response = this.ProcessHttpRequest(request);
                    }
                    catch (ObjectDisposedException)
                    {
                        break;
                    }
                    catch (InvalidDataException ide)
                    {
                        ZLoger.Warn(ide);
                        response = new HttpResponse(this);
                        response.StatusCode = HttpStatusCodes.BadRequest;
                    }
                    catch (Exception ex)
                    {
                        ZLoger.Error(ex);
                        response = new HttpResponse(this);
                        response.StatusCode = HttpStatusCodes.InternalServerError;
                    }

                    if (response != null)
                    {
                        lock (this)
                        {
                            response.Response(this._stream);
                        }

                        if (response.StatusCode == HttpStatusCodes.SwitchingProtocols)
                        {
                            //升级到了websocket
                            this._ownerHttpServer.RemoveHttpConnection(this);
                            upgradeWebSocket = true;
                            break;
                        }
                    }

                    if (this._keepAlive)
                    {
                        //保持连接则继续等待请求
                        continue;
                    }
                    else
                    {
                        //否则断开http连接
                        break;
                    }
                }
            }
            catch (SocketException)
            {
                //ZLoger.Debug(sex);
            }
            catch (IOException)
            {
                //ZLoger.Debug(ioex);
            }
            catch (Exception ex)
            {
                ZLoger.Warn(ex);
            }

            if (!upgradeWebSocket)
            {
                this.Dispose();
            }
        }

        private IHttpResponse ProcessHttpRequest(HttpRequest request)
        {
            IHttpResponse response;
            if (string.IsNullOrEmpty(request.Method))
            {
                response = new HttpResponse(request.Connection)
                {
                    StatusCode = HttpStatusCodes.BadRequest,
                    Data = new Model.DataSource(request.GetClientTextEncoding().GetBytes("请求方法不能为空"))
                };
            }
            else
            {
                //处理各种请求
                try
                {
                    response = this._requestHandler.ProcessRequest(request);
                }
                catch (Exception ex)
                {
                    ZLoger.Warn(ex);
                    response = new HttpResponse(request.Connection)
                    {
                        StatusCode = HttpStatusCodes.InternalServerError,
                        Data = new Model.DataSource(request.GetClientTextEncoding().GetBytes(ex.Message))
                    };
                }
            }

            return response;
        }

        /// <summary>
        /// 更新最后请求时间
        /// </summary>
        public void UpdateLastRequestTime()
        {
            this._lastRequestTimestamp = TimeEx.GetTimestamp();
        }

        internal bool IsExpire(long currentTimestamp, int keepMilliseconds)
        {
            if (this._keepAlive)
            {
                //保持连接永不过期
                return false;
            }

            if (currentTimestamp - this._lastRequestTimestamp > keepMilliseconds)
            {
                return true;
            }

            return false;
        }


        /// <summary>
        /// 从连接中读取数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public int Read(byte[] buffer, int offset, int count)
        {
            return this._stream.Read(buffer, offset, count);
        }



        private bool _disposed = false;
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            try
            {
                if (this._disposed)
                {
                    return;
                }
                this._disposed = true;

                this._ownerHttpServer.RemoveHttpConnection(this);
                this._stream.Dispose();

                //连接断开,可连接的tcp连接数+1
                this._args.Listener.IncrementConnections();
            }
            catch (Exception ex)
            {
                ZLoger.Error(ex);
            }
        }
    }
}
